package darknet.core.impl.net;

import darknet.core.spec.net.Message;
import darknet.core.spec.net.NetworkManager;
import darknet.core.spec.net.StreamableMessage;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import javax.net.ssl.SSLHandshakeException;

/**
 *
 * @author Yoyo
 */
public class ListenerRunnable implements Runnable {



    /** Server socket */
    private ServerSocket server;

    /** Network manager */
    private NetworkManager network;

    /** Kill command invoked */
    private boolean killInvoked;

    /** continue the inifine loop ? */
    private boolean continu;

    /**
     * Initialise a new Listerner Runnable.
     *
     * @param server
     * @param network
     */
    public ListenerRunnable(ServerSocket server, NetworkManager network) {

	if (server == null) {
	    throw new IllegalArgumentException("server is null");
	}
	this.server = server;

	if (network == null) {
	    throw new IllegalArgumentException("network is null");
	}
	this.network = network;

        continu = true;
    }

    @Override
    public void run() {

        Socket client = null;
        
	while (continu) {
	    try {

		// Waiting for a client connection.
		client = server.accept();

                // handle the connection
                Thread worker = new Thread(new HandleRunnable(client, network));
                worker.start();

            } catch (SSLHandshakeException ex) {

            } catch (SocketException ex) {

                continu = false; // Break the infinte loop

	    } catch (IOException e) {

                if (!killInvoked) {
                    throw new RuntimeException(e);
                }
	    }
	}
    }

    /**
     * Interrupt the task.
     * @throws IOException 
     */
    public void killMe() throws IOException {

        killInvoked = true;
        server.close();
    }

    /**
     *
     */
    private class HandleRunnable implements Runnable {

        /** Client socket */
        private Socket client;

        /** Network manager */
        private NetworkManager network;

        /**
         * Handle a client connection.
         *
         * @param client
         * @param network
         */
        HandleRunnable(Socket client, NetworkManager network) {

            if (client == null) {
                throw new IllegalArgumentException("client is null");
            }
            this.client = client;

            if (network == null) {
                throw new IllegalArgumentException("network is null");
            }
            this.network = network;
        }

        @Override
        public void run() {

            ObjectInputStream in = null;
            StreamableMessage message = null;

            try {
                // Read the message.
                in = new ObjectInputStream(client.getInputStream());
                message = new StreamableMessageImpl(
                        (Message) in.readObject(),
                        client.getInputStream(), client.getOutputStream());

                // Handle the message.
                network.receive(message);

            } catch (IOException ex) {
                System.err.println("IOException in HandleRunnable : " + ex);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }

        }
    }
}
